package org.testframe;

import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;

/**
 * 流程构建的相关函数
 *
 * @author 丁常磊
 * @date 2023/11/17 10:31
 */

public class FlowFunctions {

    public FlowBuilder start() {
        return new FlowBuilder();
    }

    /**
     * 初始化chrome浏览
     *
     * @param driverPath 驱动路径
     * @return
     */
    public ActionBuilder initChrome(String driverPath) {
        //然后打开网页
        return () -> flowContext -> {
            flowContext.initChrome(driverPath);
            return true;
        };
    }

    /**
     * 打开一个地址
     *
     * @param text
     * @param args
     * @return
     */
    public ActionBuilder open(String text, Object... args) {
        return () -> flowContext -> {

            //跳转地址
            String href = handleArgsText(text, args, flowContext);
            flowContext.open(href);

            return true;
        };
    }

    public ActionBuilder format(DataFormat<String, String> dataFormat, String... keys) {
        return () -> flowContext -> {

            for (String key : keys) {
                Object o = flowContext.get(key);
                if (o instanceof String) {
                    String format = dataFormat.format((String) o);
                    flowContext.set(key, format);
                }
            }

            return true;
        };
    }

    public ActionBuilder readLine(String dataSource, ReadMode readMode, String... setValue) {
        return () -> {
            return flowContext -> {
                DataSource source = flowContext.getDataSource(dataSource);
                if (source == null) {
                    throw new IllegalArgumentException("数据源[" + dataSource + "]不存在!");
                }

                //读取数据
                List<String> list = source.readLine(readMode);

                if (list == null) {
                    list = Collections.emptyList();
                }

                for (String s : setValue) {
                    String[] split = s.split("=");
                    if (split.length != 2) {
                        throw new IllegalStateException("无效的赋值表达式[" + s + "]!");
                    }

                    String exp = split[1];
                    if (!exp.startsWith("#")) {
                        throw new IllegalStateException("无效的赋值表达式[" + s + "]!");
                    }
                    String col = exp.substring(1);
                    try {
                        int i = Integer.parseInt(col) - 1;
                        String v;
                        if (list.size() <= i || i < 0) {
                            v = "";
                        } else {
                            v = list.get(i);
                        }
                        flowContext.set(split[0], v);
                    } catch (Exception e) {
                        throw new IllegalStateException("无效的赋值表达式[" + s + "]!");
                    }

                }

                return true;
            };
        };
    }


    /**
     * 关闭
     *
     * @return
     */
    public ActionBuilder close() {
        return () -> flowContext -> {
            flowContext.close();
            return true;
        };
    }


    /**
     * 输出异常
     *
     * @return
     */
    public ActionBuilder printError(String format, Object... pms) {
        return () -> flowContext -> {
            Object[] ods = new Object[pms.length];
            for (int i = 0; i < pms.length; i++) {
                Object t=pms[i];
                if (t instanceof JsExecutor) {
                    ods[i] = ((JsExecutor) t).execute(flowContext);
                }else{
                    ods[i] = t;
                }
            }
            if (format != null && !"".equals(format)) {
                System.err.println(String.format(format.replace("{}", "%s"), ods));
            }
            flowContext.getError().printStackTrace();
            return true;
        };
    }

    public DataSourceFactory csvFileSource(String filePath, int skipLine) {
        return csvFileSource(filePath, ',', Charset.defaultCharset(), skipLine);
    }

    /**
     * 创建一个数据源
     *
     * @param dataSourceFactory 数据源工厂
     * @param as                声明为
     * @return
     */
    public ActionBuilder openSource(DataSourceFactory dataSourceFactory, String as) {
        return () -> (Action) flowContext -> {
            DataSource dataSource = dataSourceFactory.create();

            //添加到数据源中
            flowContext.putDataSource(as, dataSource);

            return true;
        };

    }

    public DataSourceFactory csvFileSource(String filePath, char splitChar, Charset charset, int skipLine) {

        return () -> {

            try {
                CsvDataSource csvDataSource = new CsvDataSource(filePath, splitChar, charset);
                csvDataSource.skip(skipLine);
                return csvDataSource;

            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        };

    }


    /**
     * 等待某个元素的出现
     *
     * @return
     */
    public ActionBuilder waiteElement(ElSelector elSelector, TimeWaiteBuilder timeWaiteBuilder) {

        return () -> {
            TimeWaite timeWaite = timeWaiteBuilder.create();
            return flowContext -> {
                if (timeWaite.isEnd()) {
                    throw new IllegalStateException("查出最大等待时间! 时间:" + timeWaite + " 选择器:" + elSelector);
                }
                El select = elSelector.select(flowContext);
                return select != null;
            };
        };

    }
    /**
     * 等待某个元素的出现
     *
     * @return
     */
    public ActionBuilder waiteElement(String elKey, TimeWaiteBuilder timeWaiteBuilder) {

        return () -> {
            TimeWaite timeWaite = timeWaiteBuilder.create();
            return flowContext -> {
                ElSelector elSelector = flowContext.getSelector(elKey);
                if (elSelector == null) {
                    throw new IllegalStateException("未声明元素[" + elKey + "]");
                }
                if (timeWaite.isEnd()) {
                    throw new IllegalStateException("查出最大等待时间! 时间:" + timeWaite + " 选择器 [" + elKey + "]" + elSelector);
                }
                El select = elSelector.select(flowContext);
                return select != null;
            };
        };

    }

    /**
     * 对某个元素输入
     *
     * @param elSelector
     * @param text
     * @return
     */
    public ActionBuilder inputEl(ElSelector elSelector, String text) {
        return () -> flowContext -> {
            El select = elSelector.select(flowContext);
            if (select == null) {
                throw new IllegalStateException("没有找到元素!");
            }
            select.input(text);
            return true;
        };
    }


    public ActionBuilder inputEl(ElSelector elSelector, JsExecutor jsExecutor) {
        return () -> flowContext -> {
            El select = elSelector.select(flowContext);
            if (select == null) {
                throw new IllegalStateException("没有找到元素!");
            }
            select.input((String) jsExecutor.execute(flowContext));
            return true;
        };
    }
    /**
     * 对某个元素输入
     *
     * @param elKey
     * @param text
     * @return
     */
    public ActionBuilder inputEl(String elKey, String text) {
        return () -> flowContext -> {
            ElSelector elSelector = flowContext.getSelector(elKey);
            if (elSelector == null) {
                throw new IllegalStateException("未声明元素[" + elKey + "]");
            }
            El select = elSelector.select(flowContext);
            if (select == null) {
                throw new IllegalStateException("没有找到元素!");
            }
            select.input(text);
            return true;
        };
    }


    public ActionBuilder inputEl(String elKey, JsExecutor jsExecutor) {
        return () -> flowContext -> {
            ElSelector elSelector = flowContext.getSelector(elKey);
            if (elSelector == null) {
                throw new IllegalStateException("未声明元素[" + elKey + "]");
            }
            El select = elSelector.select(flowContext);
            if (select == null) {
                throw new IllegalStateException("没有找到元素!");
            }
            select.input((String) jsExecutor.execute(flowContext));
            return true;
        };
    }

    public JsExecutor getValue(String key) {
        if (key.contains("'") || key.contains("\"") || key.contains("\n") || key.contains("\t")) {
            throw new IllegalArgumentException("key=[" + key + "]中出现非法字符'或\"");
        }
        return new JsExecutor("ctx.get('" + key + "')");
    }


    /**
     * 点击元素
     *
     * @param elSelector
     * @return
     */
    public ActionBuilder clickEl(ElSelector elSelector) {
        return () -> flowContext -> {
            El select = elSelector.select(flowContext);
            if (select == null) {
                throw new IllegalStateException("没有找到元素!");
            }
            select.click();
            return true;
        };
    }

    /**
     * 点击元素
     *
     * @param elKey
     * @return
     */
    public ActionBuilder clickEl(String elKey) {
        return () -> flowContext -> {
            ElSelector elSelector = flowContext.getSelector(elKey);
            if (elSelector == null) {
                throw new IllegalStateException("未声明元素[" + elKey + "]");
            }
            El select = elSelector.select(flowContext);
            if (select == null) {
                throw new IllegalStateException("没有找到元素!");
            }
            select.click();
            return true;
        };
    }

    /**
     * 创建一个秒时间
     *
     * @param sec
     * @return
     */
    public TimeWaiteBuilder timeSeconds(long sec) {
        return new TimeWaiteBuilder(sec, null, 1000);
    }


    public TimeWaiteBuilder timeSeconds(RandomNumberGenerator sec) {
        return new TimeWaiteBuilder(null, sec, 1000);
    }


    /**
     * 创建一个毫秒时间
     *
     * @param ms
     * @return
     */
    public TimeWaiteBuilder timeMillSeconds(long ms) {
        return new TimeWaiteBuilder(ms, null, 1);
    }


    public TimeWaiteBuilder timeMillSeconds(RandomNumberGenerator sec) {
        return new TimeWaiteBuilder(null, sec, 1);
    }


    /**
     * 数字生成器
     *
     * @param max
     * @return
     */
    public RandomNumberGenerator randomNumber(double max) {

        return new RandomNumberGenerator(max);

    }


    /**
     * 声明元素
     *
     * @param elBySelector
     * @param key
     * @return
     */
    public ActionBuilder alias(ElSelector elBySelector, String key) {
        return () -> (Action) flowContext -> {
            flowContext.setSelector(key, elBySelector);
            return true;
        };
    }


    /**
     * 使用css选择元素
     *
     * @param css
     * @return
     */
    public ElSelector elBySelector(String css) {
        return new ElSelector() {
            @Override
            public El select(FlowContext flowContext) {
                return flowContext.findElBySelector(css);
            }

            @Override
            public String toString() {
                return "css=" + css + "";
            }
        };
    }

    /**
     * 最后一个获取到的元素
     *
     * @return
     */
    public ElSelector lastElement() {
        return new ElSelector() {
            @Override
            public El select(FlowContext flowContext) {
                return flowContext.getLastEl();
            }

            @Override
            public String toString() {
                return "[last]";
            }
        };
    }

    public ElSelector elByXpath(String xpath) {
        return new ElSelector() {
            @Override
            public El select(FlowContext flowContext) {
                return flowContext.findElXpath(xpath);
            }

            @Override
            public String toString() {
                return "xpath=" + xpath;
            }
        };
    }

    /**
     * 使用包含的文字
     *
     * @param text
     * @return
     */
    public ElSelector elByText(String text) {
        return new ElSelector() {
            @Override
            public El select(FlowContext flowContext) {
                return flowContext.findElByText(text);
            }

            @Override
            public String toString() {
                return "text=" + text;
            }
        };
    }


    /**
     * 等待一定的毫秒数
     *
     * @return
     */
    public ActionBuilder waiteTime(TimeWaiteBuilder timeWaiteBuilder) {
        return () -> {
            TimeWaite timeWaite = timeWaiteBuilder.create();
            return flowContext -> timeWaite.isEnd();
        };
    }

    /**
     * 等待集合
     *
     * @param gather 集合点
     * @return
     */
    public ActionBuilder waiteGather(Gather gather, TimeWaiteBuilder timeWaiteBuilder) {
        return () -> {

            TimeWaite timeWaite = timeWaiteBuilder.create();

            return new Action() {
                @Override
                public boolean play(FlowContext flowContext) {
                    if (timeWaite.isEnd()) {
                        throw new IllegalStateException("查出最大等待时间!");
                    }
                    return gather.check(this);
                }
            };
        };
    }

    /**
     * 展示一段内容
     *
     * @param text
     * @return
     */
    public ActionBuilder print(String text, Object... args) {
        return () -> flowContext -> {
            System.out.println(handleArgsText(text, args, flowContext));
            return true;
        };
    }


    private String handleArgsText(String text, Object[] args, FlowContext flowContext) {
        //可以进行内容格式化
        String replace = text.replace("{}", "%s");

        String[] argTxs = new String[args.length];
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg instanceof JsExecutor) {
                argTxs[i] = ((JsExecutor) arg).execute(flowContext) + "";
            } else {
                argTxs[i] = arg + "";
            }
        }
        return String.format(replace, (Object[]) argTxs);
    }

    /**
     * 执行一段js脚本
     * 执行时, 可以访问 context或者ctx 实际访问对象 {@link JsContextVisitor}
     *
     * @param script 脚本内容
     * @return
     */
    public JsExecutor js(String script) {
        return new JsExecutor(script);
    }


    /**
     * 循环执行
     *
     * @param flowBuilder
     * @param count
     * @return
     */
    public ActionBuilder loop(int count,FlowBuilder flowBuilder) {

        if (count <= 0) {
            return () -> (Action) flowContext -> true;
        }

        return () -> {

            int[] ok = new int[]{count};

            return new Action() {
                Flow currentSubFlow;

                @Override
                public boolean play(FlowContext flowContext) {

                    //如果已经抛出了异常, 则直接终止
                    if (flowContext.getError() != null) {
                        throw new RuntimeException(flowContext.getError());
                    }

                    //如果当前流程未创建, 则创建一个流程
                    if (currentSubFlow == null) {
                        currentSubFlow = flowBuilder.flowContext(flowContext).build();
                        currentSubFlow.bindBootStarter(flowContext.getFlowBootStarter());
                    }

                    //判断是否已经终止了
                    boolean end = currentSubFlow.isEnd();

                    //如果当前流程未结束
                    if (!end) {
                        //执行一步流程
                        currentSubFlow.next();
                        //如果执行后, 子流程已经报错了, 则逻辑终止
                        if (flowContext.getError() != null) {
                            throw new RuntimeException(flowContext.getError());
                        }
                        //执行一步后发现已经结束
                        end = currentSubFlow.isEnd();

                        if (end) {
                            //执行成功数量自减
                            ok[0]--;
                            //重置自流程
                            currentSubFlow = null;
                        }
                    } else {
                        currentSubFlow = null;
                    }

                    return ok[0] <= 0;
                }
            };
        };
    }


}
